// apply模拟
// 使用一个指定的this值和数组(或类数组对象)形式的参数来调用一个函数
// function.apply(thisArg,argsArray)

//原生apply函数使用
const person = {
    name:'John'
};
function introduce(age,profession,city){
    console.log(`I am ${this.name},${age} years old,${profession} in ${city}`);
}
introduce.apply(person,[25,'developer','city'],)  //I'm John, 25 years old, developer in Beijing

/*
* 实现思路:
* 1.将函数设置对象的属性
* 2.使用该对象调用函数,传递数组参数
* 3.删除临时添加的属性
* 4.返回函数执行结果
* */

/*
* 与call方法的区别
* call方法结束参数列表
* apply方法接收参数数组
* 其他功能完全相同
* */

/*
* 1.通过Function.prototype添加myApply方法，使用所有函数可用
* 2.context参数处理，确保有有效的上下文
* 3.将当前函数(this)设置为context的属性
* 4.处理参数数组,使用eval执行函数并传递参数
* 5.删除临时属性并返回结果
* */

//es5版本
Function.prototype.myApply = function (context,argsArray){
    //如果context为null 或 undefined ,则指向全局对象
    context = context || window;
    //将当前函数(this  ===》 调用myApply的函数对象) 设置为context的临时属性fn
    context.fn = this;
    //处理参数数组
    let result;
    if(!argsArray){
        //如果没有参数数组,直接调用
        result = context.fn();
    }else{
        //如果有参数数组，使用eval拼接参数
        const args = [];
        for(let i = 0;i < argsArray.length;i++){
            args.push('argsArray[' + i + ']');
        }
        result = eval('context.fn(' + args + ')');
    }
    //删除fn临时属性
    delete context.fn;
    //返回结果
    return result;
}

// 示例解析
introduce.myApply(person,[25,'developer','city'],)
/*
* context = person {name: 'John'}
* this = introduce 函数
* argsArray = [25, 'developer', 'city']
* context.fn = this
* person  = { name: 'John', fn: function introduce(age, profession, city) { ... }  }
* eval('context.fn(argsArray[0], argsArray[1], argsArray[2])')
* person.fn(25, 'developer', 'city')
*
*
* */


//es6版本
Function.prototype.newMyApply = function (context,argsArray){
    //处理context为null 或 undefined 的情况
    context  = context || window;
    // 使用Symbol创建唯一属性名,避免覆盖原有属性
    const fnSymbol = Symbol();
    context[fnSymbol] = this;
    // 执行函数
    let result;
    if(argsArray && Array.isArray(argsArray)){
        result = context[fnSymbol](...argsArray);
    }else{
        result = context[fnSymbol]();
    }
    //删除临时属性
    delete context[fnSymbol];
    //返回结果
    return result;
}

/*
* es6版本优势:
* 使用扩展运算符(...)简化参数传递
* 使用Symbol创建唯一属性,避免属性名冲突
* 代码更简洁，可数性更强
*
* */

//实际应用:
//1.数组合并
let output = '';
const numbers = [5,6,2,3,7];
const maxNumber = Math.max.apply(null,numbers);
output.innerHTML += '1.数组最大值:' + maxNumber + '<br>';

//2.函数借用
function  Product(name,price){
    this.name = name;
    this.price = price;
}
function Food(name,price){
    Product.myApply(this,[name,price]);
    this.category = 'food';
}
const cheese = new Food('cheese',5);
output.innerHTML += '2.函数借用:' + cheese.name + '- $ ' + cheese.price + '<br>';

//3.参数数组传递
function sum(a,b,c){
    return a + b + c;
}
const numbers2 = [1,2,3];
const sumResult = sum.myApply(null,numbers2);
output.innerHTML += '3.参数数组传递:' + sumResult + '<br>';